﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using D2D2.SharedInterfaces.Networking;
using D2D2.SharedInterfaces.SharedObject;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace D2D2.Core
{
    public class PlayerManagerClient
    {
        private readonly ProgressManager _live;
        private readonly ProgressManager _act;
        private SpriteFont _font;
        private PlayerObject _player;

        private static readonly Rectangle LIVE_LOCATION = new Rectangle(40, 10, 200, 20);
        private static readonly Rectangle ACTION_LOCATION = new Rectangle(40, 35, 200, 20);

        private List<PlayerObject> _snapshots = new List<PlayerObject>();

        private Random _rand = new Random(DateTime.Now.Millisecond);

        public PlayerManagerClient(ProgressManagerFactory factory, SpriteFont font)
        {
            _live = factory.CreateProgressManager(LIVE_LOCATION);
            _act = factory.CreateProgressManager(ACTION_LOCATION);
            _font = font;
        }

        public void Init()
        {         
            _act.ProgressType = ProgressType.Purple;
        }

        public PlayerObject Player { get; set; }

        public void AddSnapshot(PlayerObject player)
        {
            if (player.Id != Global.PlayerManager.Player.Id) return;

            player.LocalTime = NetTime.Now;
            _snapshots.Add(player);

            //_snapshots.Sort((a, b) => a.MessageTime.CompareTo(b.MessageTime));

            if (_snapshots.Count > 10)
                _snapshots.RemoveAt(0);

            /*
            if (Player.CurrentAction > serverPlayer.CurrentAction)
                _act.DecreaseProgress(Player.CurrentAction - serverPlayer.CurrentAction, serverPlayer.ActionHitTime);

            if (Player.CurrentAction < serverPlayer.CurrentAction && !Player.InDecreasingAction)
                _act.SetProgress(MathHelper.Min(serverPlayer.CurrentAction, serverPlayer.CurrentAction),
                                  serverPlayer.TotalAction);

            if (Player.CurrentLive > serverPlayer.CurrentLive)
                _live.DecreaseProgress(Player.CurrentLive - serverPlayer.CurrentLive, serverPlayer.LiveHitTime);

            if (Player.CurrentLive < serverPlayer.CurrentLive && !Player.InDecreasingLive)
                _live.SetProgress(MathHelper.Min(serverPlayer.CurrentLive, serverPlayer.TotalLive),
                                  serverPlayer.TotalLive);

            Player.CurrentAction = serverPlayer.CurrentAction;
            Player.CurrentLive = serverPlayer.CurrentLive;
            Player.Dexterity = serverPlayer.Dexterity;
            Player.Energy = serverPlayer.Energy;
            Player.Intelligence = serverPlayer.Intelligence;
            Player.MaxDamage = serverPlayer.MaxDamage;
            Player.MinDamage = serverPlayer.MinDamage;
            Player.Level = serverPlayer.Level;
            Player.Money = serverPlayer.Money;
            Player.Strength = serverPlayer.Strength;
            Player.TotalAction = serverPlayer.TotalAction;
            Player.TotalLive = serverPlayer.TotalLive;
            */
        }

        private PlayerObject GetInerpolatedPlayer()
        {
            double shiftTime = NetTime.Now - GlobalConst.INTERPOLATION_SHIFT;

            PlayerObject before = null;
            PlayerObject after = null;
            for (int i = _snapshots.Count - 1; i >= 0; i--)
            {
                if (_snapshots[i].LocalTime < shiftTime)
                {
                    before = _snapshots[i];
                    break;
                }

                if (i == 0)
                {
                    after = _snapshots[i];
                    break;
                }

                if (_snapshots[i].LocalTime >= shiftTime && _snapshots[i - 1].LocalTime <= shiftTime)
                {
                    before = _snapshots[i - 1];
                    after = _snapshots[i];
                    break;
                }
            }

            PlayerObject player = null;

            if (before == null) player = after;
            if (after == null) player = before;

            if (before != null && after != null)
            {
                double weight = (shiftTime - before.LocalTime) / (after.LocalTime - before.LocalTime);
                player = before.Clone();
                player.CurrentLive = MathHelper.Lerp(before.CurrentLive, after.CurrentLive, (float) weight);
                player.CurrentAction = MathHelper.Lerp(before.CurrentAction, after.CurrentAction, (float) weight);
            }

            return player;
        }

        public void Update(GameTime gameTime)
        {
            if (_snapshots.Count == 0) return;

            Player = GetInerpolatedPlayer();

            _live.SetProgress(Player.CurrentLive, Player.TotalLive);
            _act.SetProgress(Player.CurrentAction, Player.TotalAction);

            _live.Update(gameTime);
            _act.Update(gameTime);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            _live.Draw(spriteBatch);
            _act.Draw(spriteBatch);

            string liveCaption = string.Format("{0}/{1}", (int) Player.CurrentLive, (int) Player.TotalLive);
            string actCaption = string.Format("{0}/{1}", (int) Player.CurrentAction, (int) Player.TotalAction);

            spriteBatch.DrawString(_font, liveCaption,
                                   new Vector2(
                                       LIVE_LOCATION.X + (LIVE_LOCATION.Width - _font.MeasureString(liveCaption).X)/2,
                                       LIVE_LOCATION.Y + 1), Color.White);

            spriteBatch.DrawString(_font, actCaption,
                       new Vector2(
                           ACTION_LOCATION.X + (ACTION_LOCATION.Width - _font.MeasureString(actCaption).X) / 2,
                           ACTION_LOCATION.Y + 1), Color.White);

            spriteBatch.DrawString(_font, string.Format("$: {0}$",Player.Money), GlobalConst.MONEY_LOCATION, Color.Yellow);

        }
    }
}
